from langchain.tools import Tool, StructuredTool
from langchain_ollama import OllamaLLM, ChatOllama
from langchain.agents import initialize_agent, AgentType
from langchain.memory import ConversationBufferMemory
from langchain.agents.format_scratchpad import format_to_openai_functions
from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import AgentExecutor, create_react_agent
from langchain.prompts import PromptTemplate

from config.settings import OLLAMA_BASE_URL, OLLAMA_MODEL, OLLAMA_TEMPERATURE

def create_llm():
    """创建LLM实例
    
    Returns:
        OllamaLLM: 基于Ollama的大语言模型实例
    """
    return OllamaLLM(
        # 指定 Ollama 模型名称
        model=OLLAMA_MODEL,
        # 指定 Ollama 服务器的地址，用于与模型进行通信
        base_url=OLLAMA_BASE_URL,
        # 指定Ollama模型的随机性参数
        temperature=OLLAMA_TEMPERATURE
    )

def create_llm2():
    """创建LLM实例
    
    Returns:
        ChatOllama: 基于Ollama的大语言模型实例
    """
    return ChatOllama(
        # 指定 Ollama 模型名称
        model=OLLAMA_MODEL,
        # 指定 Ollama 服务器的地址，用于与模型进行通信
        base_url=OLLAMA_BASE_URL,
        # 指定Ollama模型的随机性参数
        temperature=OLLAMA_TEMPERATURE
    )

def create_agent(tools, llm):
    """创建Agent实例, 即智能体, 用于根据用户输入和可用工具与大语言模型交互
    
    Args:
        tools (list): 工具列表
        llm (BaseLLM): 大语言模型实例
    
    Returns:
        AgentExecutor: 能够使用工具和对话的智能体实例
    """
    # 创建一个同步包装器来处理异步工具
    # def sync_wrapper(async_tool):
    #     def wrapped(*args, **kwargs):
    #         import asyncio
    #         return asyncio.run(async_tool(*args, **kwargs))
    #     return wrapped

    # # 将异步工具转换为同步工具
    # sync_tools = []
    # for tool in tools:
    #     sync_tools.append(
    #         StructuredTool(
    #             name=tool.name,
    #             description=tool.description,
    #             func=sync_wrapper(tool.func),
    #             args_schema=tool.args_schema
    #         )
    #     )

    return initialize_agent(
        # 工具列表
        tools = tools,
        # 大语言模型实例
        llm = llm,
        # 指定智能体的类型为结构化聊天零样本反应描述类型
        # 这种类型的智能体可以根据工具的描述和用户输入，零样本地决定调用哪个工具来解决问题
        # 常见的智能体类型及使用场景如下：
        # - AgentType.ZERO_SHOT_REACT_DESCRIPTION: 适用于仅根据工具描述和用户输入，零样本地决定调用哪个工具解决问题，不依赖历史对话。
        # - AgentType.REACT_DOCSTORE: 适用于与文档存储交互的场景，例如在文档中查找信息。
        # - AgentType.SELF_ASK_WITH_SEARCH: 适用于需要逐步询问并搜索信息的场景，常用于信息检索。
        # - AgentType.CONVERSATIONAL_REACT_DESCRIPTION: 适用于对话场景，能结合历史对话上下文和工具描述来决定调用工具。
        agent = AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
        # 设置为 True 以查看详细的执行过程，方便调试和监控智能体的决策过程
        verbose = True,
        # 可选参数，用于设置智能体的内存，如 ConversationBufferMemory 用于存储对话历史
        # 例如：memory=ConversationBufferMemory()
        # 当智能体类型为 CONVERSATIONAL_REACT_DESCRIPTION 时，此参数通常需要设置
        # memory = ConversationBufferMemory(),

        # 可选参数，用于设置智能体的前缀，可自定义智能体的提示信息
        # 例如：agent_kwargs={"prefix": "这是自定义的提示信息"}
        # agent_kwargs=None,

        # 可选参数，用于设置智能体的最大迭代次数，避免无限循环
        # 例如：max_iterations=5，表示智能体最多可以进行 5 次迭代
        # max_iterations=None,
        max_iterations=3,

        # 可选参数，用于设置智能体在达到最大迭代次数时是否发出错误
        # 例如：handle_parsing_errors=True
        # handle_parsing_errors=None
    )
